# vim: tabstop=4 : expandtab : shiftwidth=4

import yaml
import socket
import os
import random
from calculator import *
from races import raceMaker
import color
from generator import ItemDropper

class Thing:
    """
Base object from which all things, rooms, npcs, players, boxes, potions, etc. derive.

Contains the following important items:
    Name, desc, location (UID), attrs (set), contents (set of UIDs)
"""
    List = {}
    def __init__(self):
        """
Auto-add self to Thing.List, assign a new UID, and initialize variables.
"""
        # Assign a UID one higher than the current highest uid
        if len(Thing.List) == 0:
            self.uid = 0
        else:
            # Fill Thing.List instead of appending
            for num in Thing.List.keys():
                if num+1 not in Thing.List.keys():
                    self.uid = num+1
                    break
        Thing.List[self.uid] = self

        self.name = "Unnamed"
        self.desc = "No description."
        self.location = -1

        self.attrs = set()
        self.contents = set() # Set of UIDs

    def moveInto(self, newLocationUID):
        currentLocation = fetch(self.location)
        if currentLocation != None:
            currentLocation.contents.discard(self.uid)
        newLocation = fetch(newLocationUID)
        if newLocation != None:
            self.location = newLocation.uid
            newLocation.contents.add(self.uid)
        else:
            self.hear("That exit is broken! Ask a wizard to fix it.", color.red)
            return

    def hear(self, msg="", color=""):
        pass

    def broadcast(self, msg, color="", *ignore):
        for uid in self.contents:
            thing = fetch(uid)
            if thing == None:
                continue
            if thing in ignore:
                continue
            if 'player' not in thing.attrs:
                continue
            thing.hear(msg, color)

    def tick(self): pass

    def end(self):
        "Remove self from the world."
        self.moveInto(None)
        del Thing.List[self.uid]

class Takeable(Thing):
    def __init__(self):
        Thing.__init__(self)
        self.attrs.add("takeable")

class Equipable(Thing):
    def __init__(self):
        Thing.__init__(self)

        self.desc = "You can wear this..."
        self.attrs.add("takeable")
        self.attrs.add("equipable")
        self.charges = 0
        self.ability = {}

        self.arch = ""
        self.slot = ""
        self.bonus = {
            "str": 0,
            "dex": 0,
            "int": 0,
            "con": 0,
            "dodge": 0,
            "accuracy": 0,
            "threat": 0,
            "maxHP":0,
            "maxMP":0,
            "damage":0,
            "DR":0,
            "weight":0,
        }

class Letter(Thing):
    def __init__(self):
        Thing.__init__(self)
        self.attrs.add("letter")
        self.name = "letter"
        self.sender = ""
        self.target = ""

class Mailbox(Thing):
    def __init__(self):
        Thing.__init__(self)
        self.attrs.add("mailbox")
    def sendMail(self, actor):
        if "player" not in actor.attrs: return
        actor.hear("Who will this mail be sent to?", color.gray)
        recipient = actor.input()
        if recipient == None: return
        actor.hear("You mark the letter 'To "+recipient+"'.")
        actor.hear("What do you write on the letter?")
        letterdesc = actor.input()
        if letterdesc == None: return
        actor.hear("You sign the letter 'Sincerely, "+actor.name+"'.")
        actor.hear("Is the above correct [y/n]?")
        yesorno = actor.input()
        if yesorno == None: return
        if yesorno in ["y", "yes"]:
            letter = Letter()
            letter.sender = actor.name
            letter.target = recipient
            letter.desc = letterdesc
            letter.moveInto(self.uid)
            actor.hear("You place the letter in the mailbox.", color.green)
        else:
            actor.hear("You crumple up the letter and throw it away.", color.red)
    def checkMail(self, actor):
        if "player" not in actor.attrs: return
        actor.hear("You open the slot marked "+actor.name+".", color.green)
        for uid in self.contents:
            thing = fetch(uid)
            if thing == None: continue
            if "letter" in thing.attrs:
                if thing.target.lower() == actor.name.lower():
                    actor.hear("You got a letter!", color.cyan)
                    thing.moveInto(actor.uid)
                    return
        actor.hear("There is no mail here for you.", color.red)
class NPC(Thing):
    def __init__(self):
        Thing.__init__(self)
        self.HP = 9999999
        self.attrs.add("npc")
        self.training = [] # A list of skills the NPC can offer training for
        self.quests = []

class Room(Thing):
    """
Very few differences from the base Thing object.
Defines a dictionary of exits like so:
{
    "west": 25,
    "east": 15,
    "south": 4,
    "north": 32,
    "up": 22,
}
Where each of the values is the UID of another room.
"""
    def __init__(self):
        Thing.__init__(self)
        self.exits = {}
        self.attrs.add("room")
        self.biome = ""
        self.gil = 0
        self.goods = {}
        self.mats = [] # list of crafting materials that could spawn in room
        self.doors = {}
        self.devices = [] # List of devices in the room as a string. 
        self.req2Pass = {} # Items such as rope and grapple.

class Person(Thing):
    """
A class that monsters, npcs, and players should derive from. This class has
the capability to engage in combat, and defines a .tick() routine for that purpose.
"""
    def __init__(self):
        Thing.__init__(self)
        self.combatTarget = -1 #This will be a UID!
        self.inCombat = False
        self.autoAttack = False
        self.timers = {'combat':0, 'regentick':0,
                       'ranged':100, 'cooldown':0}
        self.spellTimers = {}
        self.meditating = False
        self.resting = False
        self.restroom = None
        self.oldTarget = ""
        self.HP = 0
        self.baseHP = 0
        self.MP = 0
        self.baseMP = 0
        self.XP = 0
        self.XPToLevel = 0
        self.XPPrevious = 0
        self.old_XP = 0
        self.deathexp = 1
        self.maxHP = 0
        self.maxMP = 0
        self.level = 1
        self.oldlevel = 1 #for mob creation/levelup
        self.race = None
        self.gil = 0
        self.mats = []
        self.stats = {'threat':0,'statpoints':0,
                      'weight':0,'encumbrance':0,}
        self.skills = {}
        
        self.baseStats = {'str':0, 'dex':0, 'int':0, 'con':0} # Eventually need to have levelup calc from these 
        self.combatstats = {'hit':0, 'miss':0, 'dmglist':[], 'rndcounter':0, 'criticals':0, 'dodge':0, 'defending':0}
        self.paths = []

        self.eq = {
            'Light':None,'head':None, 'face':None, 'neck':None, 'feet':None,
            'ears':None, 'torso':None, 'fingers':None, 'waist':None, 'legs':None,
            'mainhand': None, 'offhand': None,
        }
        self.blessSpells = []
        self.spellBonus = {"dodge": 0,"accuracy": 0,"damage":0,"DR":0, "encumbrance":0} #other spell bonuses can be applied directly to the source
        self.attrs.add("person")
        self.miscAttrs = {'sneaking':False, 'hiding':False, 'held':0} 
            # Sneaking masks movement and prompts hiding - Hiding masks you from room.contents unless searched
        self.settings = {'promptBar':True}

        self.partylist = [self.uid]
        self.inparty = False
        self.invitedby = None
        self.following = False
        self.followers = set()

        self.Qflags = []

        self.biome_set = None # Remembers last created biome for faster room creation

    def die(self):
        "Revive at full health/mana."
        self.HP = self.maxHP
        self.MP = self.maxMP
        self.inCombat = False
        self.moveInto(0)
    
    def deathPile(self):
        itemList = []
        for uid in self.contents:
            item = fetch(uid)
            if item == None: continue
            if 'equipped' in item.attrs:
                if "souldbound" not in item.attrs:
                    itemList.append(item)
        from action import unequip, drop
        for item in itemList:
            unequip(self, item.name, True)
            drop(self, item.name, True)
            if random.random() <.50: item.attrs.add("hiding") # 50% chance the item will become 'buried treasure'.
        room = fetch(self.location)
        room.gil += self.gil
        self.gil == 0

    def deathCheck(self, target):
        room = fetch(target.location)
        if target.HP <= 0:
            self.inCombat = False
            self.autoAttack = False
            self.timers['combat'] = 0
            target.combatTarget = None
            if 'player' in target.attrs:
                room.broadcast(target.name+" falls to the ground, dead!", color.yellow, target)
                target.hear("You fall to the ground and die!", color.yellow)
                target.deathPile()
                target.die()
                self.stats['threat'] = 0
                target.stats['threat'] = 0
                if 'mob' in self.attrs:
                    self.XP += target.level * 50
                    levelup(self, self.stats['con'], self.stats['int'])
            if 'mob' in target.attrs:
                target.inCombat = False
                target.autoAttack = False
                target.dead = True
                room.broadcast(target.name+" has been slain!", color.yellow, target)
                room.gil += target.gil
                target.deathPile()
                expGain(self, target)
                ItemDropper.chooser(self, target)
                target.end()
                self.stats['threat'] = 0
                target.stats['threat'] = 0
                return # Important return

    def tick(self):
        """
Perform an attack if inCombat
Increase health if resting
Increase mana if meditating
Increase ranged timer
"""
        #generic regen tick
        self.timers['regentick'] += 1
        if self.timers['regentick'] > 206: self.timers['regentick'] = -5
        if self.timers['regentick'] > 201 and self.HP < self.maxHP or \
        self.timers['regentick'] > 201 and self.MP < self.maxMP:
            self.timers['regentick'] = 0
            self.HP += self.stats['con']/3
            if self.HP > self.maxHP: self.HP = self.maxHP
            self.MP += self.stats['int']/3
            if self.MP > self.maxMP: self.MP = self.maxMP
            prompt(self)
        
        from spells import cast,spellmaker

        #tick for all spells
        
        for spell in self.spellTimers:
            self.spellTimers[spell]['tick'] += 1
        for spell in self.spellTimers:
            name = self.spellTimers[spell]['name']
            if 'effect' not in self.spellTimers[spell]:
                if self.spellTimers[spell]['tick'] > self.spellTimers[spell]['duration']:
                    self.blessSpells.remove(name)
                    self.spellTimers.pop(self.spellTimers['tick'+spell]['name'])
                    self.spellTimers.pop(name)
                    cast(self, "dispell"+name)
                    break
            if 'effect' in self.spellTimers[spell]:
                if self.spellTimers[spell]['tick'] > self.spellTimers[spell]['duration']:
                    if 'HP' in self.spellTimers[spell]['effect']:
                        self.HP += self.spellTimers[spell]['effect']['HP']
                        if self.HP > self.maxHP: self.HP = self.maxHP
                    if 'MP' in self.spellTimers[spell]['effect']:
                        self.MP += self.spellTimers[spell]['effect']['MP']
                        if self.MP > self.maxMP: self.MP = self.maxMP
                    self.spellTimers[spell]['tick'] = 0
                    prompt(self)
                    break
        #spell cooldown timer
        self.timers['cooldown'] -= 1
        if self.timers['cooldown'] < 0:
            self.timers['cooldown'] = 0
        
        #Check if self is doing ranged attack, and then increase timer (ready their weapon)
        if 'player' in self.attrs:
            self.timers['ranged'] += 1
            if self.timers['ranged'] >= int((self.stats['dex']*-1.1)+60+2):
                self.timers['ranged'] = int((self.stats['dex']*-1.1)+60+1)
            if self.timers['ranged'] == int((self.stats['dex']*-1.1)+60):
                self.timers['ranged'] = int((self.stats['dex']*-1.1)+60+1)
                if self.eq['mainhand'] != None:
                    weapon = fetch(self.eq['mainhand'])
                    self.hear("You have readied your "+weapon.name+"!", color.yellow)
                elif self.eq['offhand'] != None:
                    weapon = fetch(self.eq['offhand'])
                    self.hear("You have readied your "+weapon.name+"!", color.yellow)

        room = fetch(self.location)
        if not room:
            return
        person_stat_updater(self) #keeps stats updated in case player changes equipment or spells

        #Be sure 'self' attributes are correct, and check if attacker can attack
        if self.HP > self.maxHP: self.HP = self.maxHP
        if self.MP > self.maxMP: self.MP = self.maxMP
        if self.timers['combat'] < attackSpeed(self):
            self.timers['combat'] +=1
        if self.inCombat:
            target = fetch(self.combatTarget)
            if target == None: return
            if self.autoAttack == True:
                if target == None:
                    self.inCombat = False
                    self.combatTarget = -1
                    return
        if self.timers['combat'] >= attackSpeed(self) and self.inCombat:
            if "person" not in target.attrs:
                self.hear("You cannot attack an inanimate object.", color.red)
                self.inCombat = False
                self.combatTarget = -1
                return
            if target.location != self.location:
                self.hear(target.name+" is no longer in the room!", color.red)
                self.inCombat = False
                self.combatTarget = -1
                return
            if target.miscAttrs['hiding'] == True:
                self.hear("There is nothing here of that name.", color.red)
                self.inCombat = False
                self.combatTarget = -1
                return
            selfRoom = fetch(self.location)
            targetRoom = fetch(target.location)
            if target.stats['dodge']+random.randint(-10, 6) > self.stats['accuracy']:
                self.hear("Your swing is avoided!")
                self.combatstats['miss'] += 1
                self.combatstats['rndcounter']+=1
                if 'player' in target.attrs:
                    target.hear(self.name+" swings at you, but you dodge out of the way!")
                    target.combatstats['dodge'] += 1
                    target.combatstats['defending'] += 1
                if targetRoom:
                    #if 'player' in target.attrs:
                    targetRoom.broadcast(target.name+" dodges "+self.name+"'s attack!", "", self, target)
                self.timers['combat'] = 0
                self.stats['threat'] += (self.level/2)+1
            else:

                # Engage in combat round
                dmg = attackDamage(self, target)
                target.HP -= dmg
                self.miscAttrs['sneaking'] = False
                self.miscAttrs['hiding'] = False
                #Display combat round messages
                if target.resting == True:
                    target.hear(self.name+'s attack disrupts your rest')
                    target.resting = False
                if target.meditating == True:
                    target.hear(self.name+'s attack disrupts your meditation')
                    target.meditating = False
                self.hear(color.red+"You "+self.miscAttrs['critMsg']+getAttackType(self, '1st')+color.reset+" "+target.name+color.red+" for "+color.reset+str(dmg)+color.red+" damage!", color.reset)
                target.hear(self.name+" "+color.red+self.miscAttrs['critMsg2']+getAttackType(self, '3rd')+" you for "+color.reset+str(dmg)+color.red+" damage!", color.reset)
                prompt(target, self)
                if targetRoom:
                    targetRoom.broadcast(self.name+" "+color.red+self.miscAttrs['critMsg2']+getAttackType(self, '3rd')+" "+color.reset+target.name+color.red+" for "+color.reset+str(dmg)+color.red+" damage!", "", self, target)
                #Reset combat information for next round, and log stats
                self.timers['combat'] = 0
                self.combatstats['rndcounter']+=1
                self.combatstats['hit']+=1
                self.combatstats['dmglist'].append(dmg)
                self.stats['threat'] += dmg
                target.combatstats['defending'] += 1
                #Check if target is dead. If so, revive the target and clean up combat.
                self.deathCheck(target)

        #resting tick
        if self.resting:
            room = fetch(self.location)
            if self.restroom != room:
                self.hear("You are no longer resting.")
                self.resting = False
            if self.HP >= self.maxHP:
                self.HP = self.maxHP
                self.resting = False
                self.hear("You feel fully rested.")
                prompt(self)
            self.timers['rest'] += 1
            if self.resting and self.timers['rest'] > 22: 
                if self.HP < self.maxHP:
                    self.HP += 1+int(self.stats['con']*random.uniform(.1, .4))
                    self.timers['rest'] = 0
                if self.HP <= self.maxHP: prompt(self)

        #meditating tick
        if self.meditating:
            room = fetch(self.location)
            if self.restroom != room:
                self.hear("You are no longer meditating.")
                self.meditating = False
            if self.MP >= self.maxMP:
                self.MP = self.maxMP
                self.meditating = False
                self.hear("You have regained your focus.")
                prompt(self)
            self.timers['rest'] += 1
            if self.meditating and self.timers['rest'] > 22:
                if self.MP < self.maxMP:
                    self.MP += 1+int(self.stats['int']*random.uniform(.1, .4))
                    self.timers['rest'] = 0
                if self.MP <= self.maxMP: prompt(self)

class Player(Person):
    """
Differs from Person in that Player has an active connection .conn, and custom saving/loading routines.
This class should not be directly yamled.

This class keeps an internal "static" list of all Player instances. This is the only way to prevent certain hangs of the server.
This List should not be used by other parts of the program. Use fetchByAttr in conjunction with Thing.List to retrieve players.

Do not access .conn directly, use the hear() function for sending text and input() for recieving text.
"""
    List = set()
    def __init__(self, conn):
        Person.__init__(self)
        del Thing.List[self.uid]
        Player.List.add(self)
        self.conn = conn
        self.ended = False
        self.attrs.add("player")
        self.attrs.add("wizard")
        self.channels = set()

        self.autosave = False # Whether the player will automatically save on disconnect

    def hear(self, msg, msgcolor="", endline="\n\r"):
        if self.ended: return None
        try:
            if 'player' in self.attrs:
                self.conn.send(msgcolor+msg+color.reset+endline)
        except socket.error,msg:
            self.end()
            return None
        except socket.timeout:
            self.end()
            return None

    def input(self):
        if self.ended: return None
        try:
            cmd = self.conn.recv(2048)
        except socket.timeout:
            self.end()
            return None
        except socket.error,msg:
            self.end()
            return None
        return cmd.strip("\n\r\t ")

    def reset(self):
        self.partylist = [self.uid]
        self.inparty = False
        self.invitedby = None
        self.followers = set()
        self.following = False
        self.combatTarget = -1
        for uid in Thing.List:
            player = fetch(uid)
            if player == None: return
            if "player" in player.attrs:
                if self in player.followers:
                    player.followers.discard(self)
            if 'mob' in player.attrs:
                if player.combatTarget == self.uid:
                    player.combatTarget = -1
                    player.inCombat = False

    def save(self):
        """
Write everything but the connection number and the UID to players/name.yml.
Automatically .hears a success message.
"""
        if not os.path.exists("players/"):
            os.mkdir("players")
        f = open(os.path.join("players", self.name.lower()+".yml"), "w")

        # Save all attributes of self EXCEPT for the connection and the UID
        conn = self.conn
        del self.conn
        yaml.dump(self.__dict__, f)
        self.conn = conn

        self.hear(self.name+" has been sucessfully written to disk.", color.green)

        return True

    def load(self):
        """
Open players/name.yml and update __dict__ accordingly. Returns False if the player has not yet been created. Returns True on sucess.
"""
        try:
            f = open(os.path.join("players", self.name.lower()+".yml"), "r")
        except IOError:
            return False

        self.__dict__.update(yaml.load(f))
        self.ended = False
        Thing.List[self.uid] = self
        self.inCombat = False

        f.close()
        return True

    def end(self):
        """
Shutdown/close the socket and remove from Thing.List and the current room. Safe to call more than once.
If self.autosave, save the player. The autosave feature will prevent a partially completed character from being written to disk.
"""
        if not self.ended:
            self.reset()
            self.ended = True
            try: self.conn.shutdown(socket.SHUT_RDWR)
            except socket.error,msg: pass
            try: self.conn.close()
            except socket.error,msg: pass
            Thing.List[self.uid] = None
            for thing in Thing.List.values():
                if thing == None: continue
                if "player" in thing.attrs:
                    thing.hear(self.name+" has left this world.")
            Player.List.discard(self)
            if self.autosave: self.save()
            self.moveInto(None)
            print self.name,"has disconnected."

    def __del__(self):
        "If for some reason we've reached end of scope and still haven't closed the conn, close it now."
        self.end()

def fetch(uid):
    "Fetch item by UID, return None if that UID is not registered with Thing.List."
    try:
        return Thing.List[uid]
    except KeyError:
        return None

def fetchByName(name):
    """
Fetch the first object found with .name == name. Returns None if no object has that name.
Warning: Operates globally! To fetch the nearest object in the room/inventory of the actor, use findItem()
"""
    for thing in Thing.List.values():
        if thing != None:
        
            if thing.name.lower() == name: return thing
    return None

def fetchByAttr(attr):
    """
Fetches the first object found with attr in .attrs. Returns None if no object matches.
Operates globally.
"""
    for thing in Thing.List.values():
        if attr in thing.attrs: return thing
    return None

def findMatch(name, item_list):
    """
Created for internal use in command completion..
"""
    possibles = []
    x = 0 # Iterator counter and index marker
    
    # Iterate over every item in the room until one comes up a match
    for item in item_list:
        if item == None:
            continue
        if item.name.lower() == name.lower():
            possibles.append(item)
            for i in possibles:
                if i != item: possibles.remove(i)
            return possibles
            break
        try:
            if item.name[0].lower() == name[0].lower():
                for i in xrange(len(name)):
                    try:
                        if item.name[x].lower() == name[x].lower():
                            x += 1
                            if x >= len(name):
                                x = 0
                            if item not in possibles:
                                possibles.append(item)
                        else:
                            if x >= len(name):
                                x = 0
                            if item in possibles:
                                possibles.remove(item)
                                x = 0
                                break
                    except IndexError: # If item.name is shorter than what was typed - obviously not target, so delete
                        x += 1
                        if x >= len(name):
                            x = 0
                        if item in possibles:
                            possibles.remove(item)
                            x = 0
                            continue
        except IndexError: # Most commonly if a spawner is in the room with name="", now ignores spawner.
            continue
    if len(possibles) == 0:
        return None
    else:
        return possibles

def findMatchingRace(name, item_list):
    """ I realize there might be a way to do this by incorporating it with what is already
there, but I REALLY don't want to figure that out right now. 30 more lines doesn't seem that important."""
    possibles = []
    x = 0 
    for item in item_list:
        if item == None:
            continue
        if item.race.lower() == name.lower():
            possibles.append(item)
            for i in possibles:
                if i != item: possibles.remove(i)
            return possibles
            break
        try:
            if item.race[0].lower() == name[0].lower():
                for i in xrange(len(name)):
                    try:
                        if item.race[x].lower() == name[x].lower():
                            x += 1
                            if x >= len(name):
                                x = 0
                            if item not in possibles:
                                possibles.append(item)
                        else:
                            if x >= len(name):
                                x = 0
                            if item in possibles:
                                possibles.remove(item)
                                x = 0
                                break
                    except IndexError: # If item.name is shorter than what was typed - obviously not target, so delete
                        x += 1
                        if x >= len(name):
                            x = 0
                        if item in possibles:
                            possibles.remove(item)
                            x = 0
                            continue
        except IndexError: # Most commonly if a spawner is in the room with name="", now ignores spawner.
            continue
    if len(possibles) == 0:
        return None
    else:
        return possibles

def findTarget(actor, name):
    """This function for combat only. Allows player to target by race specification OR
their descriptive name"""
    room = fetch(actor.location)
    room_items = []
    #Generate list of objects in the room
    for uid in room.contents:
        item = fetch(uid)
        if item != None and 'person' in item.attrs:
            room_items.append(item)
    items = findMatch(name, room_items)
    if items != None:
        return items[0]
    else:
        player_items = []
        for uid in room.contents:
            item = fetch(uid)
            if item != None and 'person' in item.attrs\
                             and 'player' not in item.attrs:
                player_items.append(item)
        items = findMatchingRace(name, player_items)
        if items != None:
            return items[0]
        else:
            actor.hear("There is nothing here of that name.", color.red)

def findPersonInWorld(actor, name):
    """ 
Searches Thing.list for anything with 'person' in .attrs. 
No error message displayed as this should only be called in specific cases. Add error
message in function calling this if None is returned. Note that persons[0] is returned
even if there are 20 matches. Specificity is key.
"""
    personsInRoom = []
    for uid in Thing.List:
        person = fetch(uid)
        if person != None and 'person' in person.attrs:
            personsInRoom.append(person)
    persons = findMatch(name, personsInRoom)
    if persons != None:
        return persons[0]
    else: return None
    
def findItemInWorld(actor, name):
    """ 
Searches Thing.list for anything with 'takeable' in .attrs. 
No error message displayed as this should only be called in specific cases. Add error
message in function calling this if None is returned.
"""
    world_items = []
    for uid in Thing.List:
        item = fetch(uid)
        if item != None and 'takeable' in item.attrs:
            world_items.append(item)
    items = findMatch(name, world_items)
    if items != None:
        return items[0]
    else: return None

def findItem(actor, name):
    """
Find an item within the immediate area of actor matching name. Search actor's inventory after item not 
found in room contents. If no items are found matching, actor.hear() an error message and return None.
"""
    room = fetch(actor.location)
    room_items = []
    #Generate list of objects in the room
    for uid in room.contents:
        item = fetch(uid)
        if item != None:
            room_items.append(item)
    items = findMatch(name, room_items)
    if items != None:
        return items[0]
    else:
        player_items = []
        for uid in actor.contents:
            item = fetch(uid)
            if item != None:
                player_items.append(item)
        items = findMatch(name, player_items)
        if items != None:
            return items[0]
        else:
            actor.hear("There is nothing here of that name.", color.red)

def findInActor(actor, name):
    """ Call this to search the player's inventory specifically for an item
    Will eventually get the list to display, but the possibles list is being reduced to 1 for some reason"""
    player_items = []
    for uid in actor.contents:
        item = fetch(uid)
        if item != None:
            player_items.append(item)
    items = findMatch(name, player_items)
    if items != None:
        if len(items) == 1: return items[0]
    else:
        if items == None:
            actor.hear("There is nothing here of that name.", color.red)
            return
    actor.hear("Did you mean...", color.blue)
    for idx,possible in enumerate(items):
        actor.hear("    "+str(idx + 1)+") "+possible.name, color.red)
    actor.hear("Select an item <return to abort> :", color.blue)
    selection = actor.input()
    if selection == None:
        return None
    if not selection.isdigit():
        actor.hear("Abort.", color.red)
        return None
    selection = int(selection) - 1
    if selection < 0: # Player entered 0
        actor.hear("That item is not in the list.", color.red)
        return
    try:
        return items[selection]
    except IndexError:
        actor.hear("That item is not in the list.", color.red)
        return
    return None
